//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef __ELASTOS_INTERLOCK_H__
#define __ELASTOS_INTERLOCK_H__

#define __ATOMIC_H_

#define __PSR_I         __32BIT(7)
#define __PSR_F         __32BIT(6)

#if defined(_GNUC)

INLINE void __Sti()
{
    ASM("mrs    r0, cpsr;"
        "and    r0, r0, %0;"
        "msr    cpsr_c, r0;"
        : :"i"(~(__PSR_I | __PSR_F)) :"r0");
}

INLINE uint32_t __SaveFlagsAndCli()
{
    uint32_t uFlags;
    ASM("mrs    %0, cpsr;"
        "orr    r0, %0, %1;"
        "msr    cpsr_c, r0;"
        :"=r"(uFlags) :"i"(__PSR_I | __PSR_F) :"r0");
    return uFlags;
}
#elif defined(_EVC)

EXTERN_C void __Sti();
EXTERN_C uint32_t __SaveFlagsAndCli();

#else

#error Unknown C++ compiler

#endif // defined(_GNUC)

INLINE void __RestoreIF(uint_t uFlags)
{
    if (0 == (uFlags & (__PSR_I | __PSR_F))) __Sti();
}

#define InterlockedIncrement(x)             __InterlockedIncrement(x)
#define InterlockedDecrement(x)             __InterlockedDecrement(x)
#define InterlockedExchangeAdd(x, y)        __InterlockedExchangeAdd(x, y)
#define InterlockedExchange(x, y)           __InterlockedExchange(x, y)
#define InterlockedExchangePointer(x, y)    __InterlockedExchangePointer(x, y)
#define InterlockedCompareExchange(x,y,z)   __InterlockedCompareExchange(x,y,z)
#define InterlockedCompareExchangePointer(x, y, z) \
            __InterlockedCompareExchangePointer(x, y, z)

INLINE Int32 __InterlockedExchangeAdd(PInt32 plAddend, Int32 lIncrement)
{
    Int32 lOriginalAddend;

    uint32_t uFlags = __SaveFlagsAndCli();
    lOriginalAddend = *plAddend;
    *plAddend += lIncrement;
    __RestoreIF(uFlags);

    return lOriginalAddend;
}

#if defined(_GNUC)

INLINE Int32 __InterlockedExchange(PInt32 plTarget, Int32 lValue)
{
    Int32 lOriginalTarget;

#ifdef _arm11  //not support simple swp instruction
    uint32_t uFlags = __SaveFlagsAndCli();
    lOriginalTarget = *plTarget;
    *plTarget = lValue;
    __RestoreIF(uFlags);
#else
    ASM ("swp    %0, %1, [%2];"
        :"=&r"(lOriginalTarget)
        :"r"(lValue), "r"(plTarget));
#endif

    return lOriginalTarget;
}

INLINE PVoid __InterlockedExchangePointer(PVoid *ppvTarget, PVoid pvValue)
{
    PVoid pvOriginalTarget;

    ASM("swp    %0, %1, [%2];"
        :"=&r"(pvOriginalTarget)
        :"r"(pvValue), "r"(ppvTarget));

    return pvOriginalTarget;
}
#elif defined(_EVC)

EXTERN_C Int32 __InterlockedExchange(PInt32 plTarget, Int32 lValue);
EXTERN_C PVoid __InterlockedExchangePointer(PVoid *ppvTarget, PVoid pvValue);

#else

#error Unknown C++ compiler

#endif // defined(_GNUC)

INLINE Int32 __InterlockedCompareExchange(PInt32 plDestination,
                Int32 lExchange, Int32 lComperand)
{
    Int32 lOriginalDestination;

    uint32_t uFlags = __SaveFlagsAndCli();
    lOriginalDestination = *plDestination;
    if (*plDestination == lComperand) {
        *plDestination = lExchange;
    }
    __RestoreIF(uFlags);

    return lOriginalDestination;
}

INLINE PVoid __InterlockedCompareExchangePointer(PVoid *ppvDestination,
                PVoid pvExchange, PVoid pvComperand)
{
    PVoid pvOriginalDestination;

    uint32_t uFlags = __SaveFlagsAndCli();
    pvOriginalDestination = *ppvDestination;
    if (*ppvDestination == pvComperand) {
        *ppvDestination = pvExchange;
    }
    __RestoreIF(uFlags);

    return pvOriginalDestination;
}

INLINE Int32 __InterlockedIncrement(PInt32 plAddend)
{
    Int32 lIncrementedAddend;

    uint32_t uFlags = __SaveFlagsAndCli();
    lIncrementedAddend = ++(*plAddend);
    __RestoreIF(uFlags);

    return lIncrementedAddend;
}

INLINE Int32 __InterlockedDecrement(PInt32 plAddend)
{
    Int32 lDecrementedAddend;

    uint32_t uFlags = __SaveFlagsAndCli();
    lDecrementedAddend = --(*plAddend);
    __RestoreIF(uFlags);

    return lDecrementedAddend;
}

#endif //__ELASTOS_INTERLOCK_H__
